package com.tskj.leonsu.service.ad;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Hashtable;
import java.util.ResourceBundle;

@Service
public class AdService {

    private LdapContext lctx = null;

    private String LdapLastError = "";

    private String adDomain;

    private String adHost;

    private String adPort;

    public AdService() {
        ResourceBundle res = ResourceBundle.getBundle("config.system.system");
        adDomain = res.getString("adDomain").trim();
        adHost = res.getString("adHost").trim();
        adPort = res.getString("adPort").trim();
//        System.out.println(adHost);
    }
    public int adLogin(String userName, String password) {
        boolean logged = userName != null && userName != "" && password != null
                && password != "";
        if (logged) {
            try {
                System.out.println("开始AD认证");
                lctx = ldapConnect(userName, password);
                if (lctx == null) {
                    logged = false;
                }
            } catch (Exception e) {
                // 不明错误
                e.printStackTrace();
                logged = false;
            } finally {
                ldapDisconnect();
            }
        }

        if (logged) {
            // 成功
            return 0;
        } else {
            // 失败
            System.out.println(LdapLastError);
            if (LdapLastError.contains("LdapErr: DSID-0C09030F") || LdapLastError.contains("data 525")) {
                // 用户不存在
                return 2;
            }
            if (LdapLastError.contains("LdapErr: DSID-0C0903A8") || LdapLastError.contains("data 52e")) {
                // 密码错误
                return 3;
            }
            if (LdapLastError.contains("data 111")) {
                // 教师登陆
                return 4;
            }
            return 1;
        }
    }

    public JSONObject getADUserInfo(String userName, String password) {
        // 邮箱的后缀名
        String domain = adDomain;
        domain = domain.replace("@", "DC=").replace(".", ",DC=");

        System.out.println("开始获取AD资料");
        String uid = userName;
        String filter = "(&(objectClass=top)(objectClass=organizationalPerson)(sAMAccountName="
                + uid + "))";
        // String filter =
        // "(&(objectClass=top)(objectClass=organizationalPerson))";
        // 限制要查询的字段内容
        String[] attrPersonArray = {"sAMAccountName", "displayName",
                "description"};
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        // 设置将被返回的Attribute
        searchControls.setReturningAttributes(attrPersonArray);

        NamingEnumeration<SearchResult> answer;
        JSONObject json = new JSONObject();
        String value;
        try {
            lctx = ldapConnect(userName, password);
            if (lctx == null) {
                return null;
            }
            answer = lctx.search(domain, filter, searchControls);
            System.out.println("answer=" + answer.hasMore());
            // 用户类型，用于判断是学生还是教师
            String userType = "老师";
            if (answer.hasMore()) {
                SearchResult result = answer.next();
                NamingEnumeration<? extends Attribute> attrs = result
                        .getAttributes().getAll();
                while (attrs.hasMore()) {
                    Attribute attr = attrs.next();
                    if ("objectSid".equals(attr.getID())) {
                        value = getObjectSid(attr.get());
                    } else if ("objectGUID".equals(attr.getID())) {
                        value = getGUID(attr.get());
                    } else if ("userParameters".equals(attr.getID())) {
                        // getUserParameters(attr.get().toString());
                        value = URLEncoder.encode(attr.get().toString(),
                                "utf-8");
                    } else {
                        value = attr.get().toString();
                    }
                    System.out.println(attr.getID() + "=" + value);

                    json.put(attr.getID(), value);
                    // 根据学号字段判断是学生还是老师，需要修改
                    if ("description".equals(attr.getID())) {
                        userType = attr.get().toString().isEmpty() ? "老师"
                                : "学生";
                        if ("学生".equals(userType)) {
                            // 如果是学生，usertype=0
                            json.put("usertype", 0);
                        } else if ("老师".equals(userType)) {
                            // 如果是老师，usertype=1
                            json.put("usertype", 1);
                        } else {
                            // 如果是其他，usertype=2
                            json.put("usertype", 2);
                        }
                    }
                }
                json.put("code", 0);
                System.out.println("============");
            } else {
                json.put("code", 1);
                json.put("errMsg", "未找到用户信息");
            }
            System.out.println(json.toJSONString());
        } catch (NamingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            json.put("code", 2);
            json.put("errMsg", e.getMessage());
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            ldapDisconnect();
        }
        // 输出查到的数据
        return json;
    }

    private LdapContext ldapConnect(String userName, String password) {
        String url = String.format("ldap://%s:%s", adHost, adPort);
        String user = userName.contains(adDomain) ? userName : userName + adDomain;
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        // 不带邮箱后缀名的话，会报错，具体原因还未探究。高手可以解释分享。
        env.put(Context.SECURITY_PRINCIPAL, user);
        env.put(Context.SECURITY_CREDENTIALS, password);
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, url);
        LdapContext lctx = null;
        try {
            lctx = new InitialLdapContext(env, null);
            LdapLastError = "";
        } catch (AuthenticationException e) {
            System.out.println("Login Ldap Server Failed...(" + userName + ")");
//            e.printStackTrace();
            LdapLastError = e.getMessage();
        } catch (Exception e) {
//            System.out.println("Login Ldap Server Wrong...\n " + e.toString());
            LdapLastError = e.toString();
        }

        return lctx;
    }

    private void ldapDisconnect() {
        if (lctx != null) {
            try {
                lctx.close();
            } catch (NamingException e) {
                e.printStackTrace();
            }
        }
    }

    private String AddLeadingZero(int k) {
        return (k <= 0xF) ? "0" + Integer.toHexString(k) : Integer
                .toHexString(k);
    }

    private String getGUID(Object obj) {
        byte[] GUID = obj.toString().getBytes();
        String strGUID = "";

        strGUID = strGUID + AddLeadingZero((int) GUID[3] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[2] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[1] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[0] & 0xFF);
        strGUID = strGUID + "-";
        strGUID = strGUID + AddLeadingZero((int) GUID[5] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[4] & 0xFF);
        strGUID = strGUID + "-";
        strGUID = strGUID + AddLeadingZero((int) GUID[7] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[6] & 0xFF);
        strGUID = strGUID + "-";
        strGUID = strGUID + AddLeadingZero((int) GUID[8] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[9] & 0xFF);
        strGUID = strGUID + "-";
        strGUID = strGUID + AddLeadingZero((int) GUID[10] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[11] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[12] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[13] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[14] & 0xFF);
        strGUID = strGUID + AddLeadingZero((int) GUID[15] & 0xFF);

        return strGUID;
    }

    private String getObjectSid(Object object) throws NamingException {
        byte[] SID = object.toString().getBytes();
        // byte[] SID = object.toString().getBytes();//不可以用这种转换类型，有损耗
        StringBuilder strSID = new StringBuilder("S-");
        strSID.append(SID[0]).append('-');
        // bytes[2..7] :
        StringBuilder tmpBuff = new StringBuilder();
        for (int t = 2; t <= 7; t++) {
            String hexString = Integer.toHexString(SID[t] & 0xFF);
            tmpBuff.append(hexString);
        }
        strSID.append(Long.parseLong(tmpBuff.toString(), 16));
        // bytes[1] : the sub authorities count
        int count = SID[1];
        // bytes[8..end] : the sub authorities (these are Integers - notice
        // the endian)
        for (int i = 0; i < count; i++) {
            int currSubAuthOffset = i * 4;
            tmpBuff.setLength(0);
            tmpBuff.append(String.format("%02X%02X%02X%02X",
                    (SID[11 + currSubAuthOffset] & 0xFF),
                    (SID[10 + currSubAuthOffset] & 0xFF),
                    (SID[9 + currSubAuthOffset] & 0xFF),
                    (SID[8 + currSubAuthOffset] & 0xFF)));
            strSID.append('-').append(Long.parseLong(tmpBuff.toString(), 16));

        }
        return strSID.toString();
    }
}
